﻿using System;
using System.Windows;
using System.Net;
using System.Net.Sockets;
using System.Threading;
using FinosLibrary;
using System.Text;
using System.ComponentModel;
using System.IO;

namespace FinosLibrary {
    public class NetConnection : ModelLogic {
        public string byteHead;
        public string appName;
        public Socket socket;
        private MemoryStream buffer;
        private int headInfo;
        private int waitSize;
        private byte[] headBytes;
        public string _log;
        public string log { get { return _log; } set { _log += value; this.NotifyPropertyChanged(x => x.log); } }
        protected NetConnection()
            : base() {
        }
        
        public void Connect(EndPoint endPoint, string appname) {
            appName = appname.Split('/')[0];
            if (socket != null && socket.Connected) {
                socket.Close();
            }
            socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            headInfo = 0;
            waitSize = 4;
            SocketAsyncEventArgs e = new SocketAsyncEventArgs();
            e.RemoteEndPoint = endPoint;
            e.Completed += (object sender, SocketAsyncEventArgs args) => {
                if (args.SocketError == SocketError.Success) {
                    call("connect", appname);
                    SocketAsyncEventArgs receiveArgs = new SocketAsyncEventArgs();
                    receiveArgs.SetBuffer(new byte[1024], 0, 1024);
                    receiveArgs.Completed += (object rs, SocketAsyncEventArgs ra) => {
                        ProcessReceive(ra);
                    };
                    socket.ReceiveAsync(receiveArgs);
                } else {
                    callLocal("NetConnection_Faild", args.SocketError);
                }
            };
            try {
                socket.ConnectAsync(e);
            } catch (Exception exc) {
                callLocal("NetConnection_Faild", exc.Message);
            }
        }
        public void ChangeRoom(string roomName) {
            if (socket.Connected) {
                call("connect", appName + "/" + roomName);
            }
        }
        public void SendPackage(Package p) {
            SocketAsyncEventArgs e = new SocketAsyncEventArgs();
            byte[] result = p.toBytes();
            e.SetBuffer(result, 0, result.Length);
            socket.SendAsync(e);
        }
        private void readData(MemoryStream stream) {
            int numberOfBytesRead = 0;
            if (headInfo == 0) {
                if (waitSize == 4) headBytes = new byte[4];
                numberOfBytesRead = stream.Read(headBytes, 4 - waitSize, waitSize);
                if (numberOfBytesRead == 0) return;
                if (numberOfBytesRead < waitSize) {
                    waitSize = waitSize - numberOfBytesRead;
                } else {
                    headInfo = BitConverter.ToInt32(headBytes, 0);
                    waitSize = headInfo;
                    buffer = new MemoryStream(headInfo);
                }
            }
            byte[] bytes = new byte[1024];
            numberOfBytesRead = stream.Read(bytes, 0, waitSize >= 1024 ? 1024 : waitSize);
            if (numberOfBytesRead == 0) return;
            buffer.Write(bytes, 0, numberOfBytesRead);
            waitSize -= numberOfBytesRead;
            if (waitSize == 0) {
                buffer.Seek(0, SeekOrigin.Begin);
                Package p;
                if (byteHead == null) {
                    p = buffer.FromJsonTo<Package>();
                } else {
                    p = new Package(byteHead) { byteBody = buffer.GetBuffer() };
                }
                //log = p.head;
                Logic.doLogic(p);
                buffer.Close();
                headInfo = 0;
                waitSize = 4;
            }
            readData(stream);
        }
        private void ProcessReceive(SocketAsyncEventArgs e) {
            if (e.BytesTransferred == 0) {
                close("服务器主动断开！"); return;
            }
            using (MemoryStream stream = new MemoryStream()) {
                stream.Write(e.Buffer, 0, e.BytesTransferred);
                stream.Seek(0, SeekOrigin.Begin);
                readData(stream);
            }
            try {
                socket.ReceiveAsync(e);
            } catch (SocketException se) {
                close(se.Message);
            }
        }
        public void close(string reason) {
            close();
            Logic.doLogic(new Package("NetConnection_Disconnected",reason));
        }
        public void close() {
            if (buffer != null) buffer.Close();
            socket.Close();
        }
    }
}
